package com.cloudweb.oa.service.impl;

import cn.js.fan.db.ListResult;
import cn.js.fan.db.SQLFilter;
import cn.js.fan.util.*;
import cn.js.fan.web.Global;
import com.cloudweb.oa.api.IModuleFieldSelectCtl;
import com.cloudweb.oa.api.INestSheetCtl;
import com.cloudweb.oa.api.IWorkflowUtil;
import com.cloudweb.oa.security.AuthUtil;
import com.cloudweb.oa.service.FlowService;
import com.cloudweb.oa.service.IDocService;
import com.cloudweb.oa.service.MacroCtlService;
import com.cloudweb.oa.utils.ConstUtil;
import com.cloudweb.oa.utils.SpringUtil;
import com.cloudweb.oa.vo.Result;
import com.cloudwebsoft.framework.util.LogUtil;
import com.redmoon.oa.Config;
import com.redmoon.oa.android.Privilege;
import com.redmoon.oa.base.IFormDAO;
import com.redmoon.oa.base.IFormMacroCtl;
import com.redmoon.oa.dept.DeptDb;
import com.redmoon.oa.dept.DeptUserDb;
import com.redmoon.oa.flow.*;
import com.redmoon.oa.flow.macroctl.MacroCtlMgr;
import com.redmoon.oa.flow.macroctl.MacroCtlUnit;
import com.redmoon.oa.person.UserDb;
import com.redmoon.oa.person.UserMgr;
import com.redmoon.oa.sys.DebugUtil;
import com.redmoon.oa.ui.PortalDb;
import com.redmoon.oa.visual.FormUtil;
import com.redmoon.oa.visual.FuncUtil;
import com.redmoon.oa.visual.ModuleSetupDb;
import com.redmoon.oa.visual.ModuleUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;

@Slf4j
@Service
public class FlowServiceImpl implements FlowService {

    @Autowired
    HttpServletRequest request;

    @Autowired
    private IWorkflowUtil workflowUtil;

    @Autowired
    AuthUtil authUtil;

    @Override
    public JSONObject init(WorkflowDb wf, MyActionDb mad, FormDAO fdao, String mutilDept) {
        long t = System.currentTimeMillis();
        JSONArray fields = new JSONArray();
        JSONObject result = new JSONObject();
        boolean hasAttach = true;
        FormDb fd = fdao.getFormDb();

        String res = "0";
        String msg = "操作成功";
        int flowId = (int)mad.getFlowId();
        MacroCtlMgr mm = new MacroCtlMgr();
        JSONObject json = new JSONObject();
        try {
            wf = wf.getWorkflowDb(flowId);
            Leaf lf = new Leaf();
            lf = lf.getLeaf(wf.getTypeCode());
            hasAttach = fd.isHasAttachment();

            // 扫码传值
            String scanActionType = ParamUtil.get(request, "scanActionType");
            if (ConstUtil.SCAN_ACTION_TYPE_FLOW.equals(scanActionType)) {
                String scanTargetField = ParamUtil.get(request, "scanTargetField");
                if (!StrUtil.isEmpty(scanTargetField)) {
                    String scanId = ParamUtil.get(request, "scanId");
                    fdao.setFieldValue(scanTargetField, String.valueOf(scanId));
                    // 此处得进行保存操作，因为下面表单域选择宏控件autoMap中会重新获取fdao，且后面在本方法中进行了fdao.load()操作
                    fdao.save();
                    DebugUtil.w(getClass(), "init", "scanId=" + scanId);
                } else {
                    DebugUtil.w(getClass(), "init", "scanTargetField 不存在");
                }
            }

            // 处理表单域自动映射
            Vector<FormField> v = fdao.getFields();
            Iterator<FormField> ir = v.iterator();
            while (ir.hasNext()) {
                FormField ff = ir.next();
                if ("macro".equals(ff.getType())) {
                    MacroCtlUnit mu = mm.getMacroCtlUnit(ff.getMacroType());
                    if (mu == null) {
                        log.error("MactoCtl " + ff.getTitle() + "：" + ff.getMacroType() + " is not exist.");
                        continue;
                    }

                    String macroCode = mu.getCode();
                    if ("module_field_select".equals(macroCode)) {
                        MacroCtlService macroCtlService = SpringUtil.getBean(MacroCtlService.class);
                        IModuleFieldSelectCtl moduleFieldSelectCtl = macroCtlService.getModuleFieldSelectCtl();
                        try {
                            String value = StrUtil.getNullStr(ff.getValue());
                            if ("".equals(value) || value.equals(ff.getDefaultValueRaw()) || value.equals(ff.getDescription())) {
                                if (json.has("requestParam") && !"".equals(json.getString("requestParam"))) {
                                    // 来自于指定的参数名称
                                    value = ParamUtil.get(request, json.getString("requestParam"));
                                } else {
                                    // 默认以字段名作为参数从request中获取
                                    value = ParamUtil.get(request, ff.getName());
                                }
                            }
                            // 如果value中存在值，则说明需自动映射
                            if (!"".equals(value)) {
                                moduleFieldSelectCtl.autoMap(request, flowId, value, ff);
                            }
                        } catch (JSONException e) {
                            LogUtil.getLog(getClass()).error(e);
                        }
                    }
                }
            }

            // 重新再载入一次，以免出现缓存问题
            fdao.load();

            if (Global.getInstance().isDebug()) {
                DebugUtil.i(getClass(), "init fdao.load", (System.currentTimeMillis() - t) + " ms");
            }

            UserMgr um = new UserMgr();
            // 这段用来验证字段是否可写
            WorkflowActionDb wa = new WorkflowActionDb();
            int actionId = (int) mad.getActionId();
            wa = wa.getWorkflowActionDb(actionId);
            if (wa == null || !wa.isLoaded()) {
                res = "-1";
                msg = "流程中的相应动作不存在";
                json.put("res", res);
                json.put("msg", msg);
                return json;
            }

            WorkflowPredefineDb wfp = new WorkflowPredefineDb();
            wfp = wfp.getPredefineFlowOfFree(wf.getTypeCode());

            String strIsShowNextUsers = WorkflowActionDb.getActionProperty(wfp, wa.getInternalName(), "isShowNextUsers");
            boolean isShowNextUsers = !"0".equals(strIsShowNextUsers);

            String strIsBtnSaveShow = WorkflowActionDb.getActionProperty(wfp, wa.getInternalName(), "isBtnSaveShow");
            boolean isBtnSaveShow = !"0".equals(strIsBtnSaveShow);

            String btnAgreeName = WorkflowActionDb.getActionProperty(wfp, wa.getInternalName(), "btnAgreeName");
            String btnRefuseName = WorkflowActionDb.getActionProperty(wfp, wa.getInternalName(), "btnRefuseName");

            // 取可写表单域
            String fieldWrite = StrUtil.getNullString(wa.getFieldWrite()).trim();
            String[] fds = fieldWrite.split(",");
            int len = fds.length;

            String userName = authUtil.getUserName();

            // 自由流程根据用户所属的角色，得到可写表单域
            if (lf.getType() == Leaf.TYPE_FREE) {
                WorkflowPredefineDb wfpd = new WorkflowPredefineDb();
                wfpd = wfpd.getPredefineFlowOfFree(wf.getTypeCode());

                fds = wfpd.getFieldsWriteOfUser(wf, userName);
                len = fds.length;
            }

            String fieldHide = StrUtil.getNullString(wa.getFieldHide()).trim();
            // 将不显示的字段加入fieldHide
            ir = v.iterator();
            while (ir.hasNext()) {
                FormField ff = ir.next();
                if (ff.getHide() == FormField.HIDE_EDIT || ff.getHide() == FormField.HIDE_ALWAYS) {
                    if ("".equals(fieldHide)) {
                        fieldHide = ff.getName();
                    } else {
                        fieldHide += "," + ff.getName();
                    }
                }
            }
            String[] fdsHide = fieldHide.split(",");
            int lenHide = fdsHide.length;

            MacroCtlUnit mu;
            json.put("res", "0");
            json.put("msg", "操作成功");
            json.put("sender", um.getUserDb(wf.getUserName()).getRealName());
            json.put("cwsWorkflowTitle", wf.getTitle());
            json.put("actionId", String.valueOf(actionId));
            json.put("myActionId", String.valueOf(mad.getId()));
            json.put("flowId", String.valueOf(flowId));
            json.put("url", "public/flow_dispose_do");
            json.put("formCode", fdao.getFormCode());

            json.put("isShowNextUsers", isShowNextUsers);
            json.put("isBtnSaveShow", isBtnSaveShow);
            json.put("btnAgreeName", btnAgreeName);
            json.put("btnRefuseName", btnRefuseName);

            // 是否为加签
            boolean canPlus = false;
            com.redmoon.oa.flow.FlowConfig conf = new com.redmoon.oa.flow.FlowConfig();
            if (conf.getIsDisplay("FLOW_BUTTON_PLUS") && wfp.isPlus()) {
                canPlus = true;
            }
            json.put("canPlus", canPlus);

            json.put("isFlowStarted", wf.isStarted());

            if (Global.getInstance().isDebug()) {
                DebugUtil.i(getClass(), "init isFlowStarted", (System.currentTimeMillis() - t) + " ms");
            }

            // fgf 20180814 显示控制
            // 20250111 分离
            /*String viewJs = workflowUtil.doGetViewJSMobile(request, fd, fdao, userName, false, wa);
            if (Global.getInstance().isDebug()) {
                DebugUtil.i(getClass(), "init viewJs", (System.currentTimeMillis() - t) + " ms");
            }
            json.put("viewJs", viewJs);*/

            WorkflowPredefineDb wfd = new WorkflowPredefineDb();
            wfd = wfd.getPredefineFlowOfFree(wf.getTypeCode());
            boolean isLight = wfd.isLight();
            json.put("isLight", isLight);// 判断时候是@liuchen
            json.put("isFree", lf.getType() == Leaf.TYPE_FREE);

            boolean canDel = false;
            if (conf.getIsDisplay("FLOW_BUTTON_DEL")) {
                if (WorkflowMgr.canDelFlowOnAction(request, wf, wa, mad)) {
                    canDel = true;
                }
            }
            json.put("canDel", canDel);

            // 结束流程标志
            boolean canFinishAgree = false;
            if (conf.getIsDisplay("FLOW_BUTTON_FINISH")) {
                String flag = wa.getFlag();
                if (/*wf.isStarted() && */flag.length() >= 12 && "1".equals(flag.substring(11, 12))) {
                    if (mad.getCheckStatus() != MyActionDb.CHECK_STATUS_SUSPEND) {
                        canFinishAgree = true;
                    }
                }
            }
            json.put("canFinishAgree", canFinishAgree);

            ModuleSetupDb msd = new ModuleSetupDb();
            msd = msd.getModuleSetupDb(fdao.getFormCode());

            // 遍历表单字段-------------------------------------------------
            ir = v.iterator();
            while (ir.hasNext()) {
                FormField ff = ir.next();

                // 置可写表单域
                boolean finded = false;
                for (int i = 0; i < len; ++i) {
                    if (ff.getName().equals(fds[i])) {
                        finded = true;
                        break;
                    }
                }
                if (!finded) {
                    ff.setEditable(false);
                }

                // 如果不是自由流程
                if (lf.getType() != Leaf.TYPE_FREE) {
                    // 置隐藏表单域
                    finded = false;
                    for (String s : fdsHide) {
                        if (ff.getName().equals(s)) {
                            finded = true;
                            break;
                        }
                    }
                    if (finded) {
                        log.info(
                                "field:" + ff.getTitle() + " is hidden.");
                        ff.setHidden(true);
                    }
                }

                if (ff.getType().equals(FormField.TYPE_BUTTON)) {
                    com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(ff.getDescription());
                    com.alibaba.fastjson.JSONObject jsonObj = ModuleUtil.getButtonParsed(request, jsonObject, msd, fdao, ConstUtil.PAGE_TYPE_FLOW);
                    // 无权查看时返回 null
                    if (jsonObj == null) {
                        continue;
                    }
                }

                JSONObject field = new JSONObject();
                String desc = StrUtil.getNullStr(ff.getDescription());
                field.put("title", ff.getTitle());
                field.put("code", ff.getName());
                field.put("desc", desc);

                field.put("present", StrUtil.getNullStr(ff.getPresent()));
                // 如果是计算控件，则取出精度和四舍五入属性
                if (ff.getType().equals(FormField.TYPE_CALCULATOR)) {
                    FormParser fp = new FormParser();
                    String isroundto5 = fp.getFieldAttribute(fd, ff, "isroundto5");
                    String digit = fp.getFieldAttribute(fd, ff, "digit");
                    field.put("formula", ff.getDefaultValueRaw());
                    field.put("isroundto5", isroundto5);
                    field.put("digit", digit);
                }
                String options = "";
                String macroType = "";
                String controlText = "";
                String controlValue = "";
                JSONArray opinionArr = null;
                JSONObject opinionVal = null;

                String macroCode = "";

                String metaData = "";

                JSONArray js = new JSONArray();
                if ("macro".equals(ff.getType())) {
                    mu = mm.getMacroCtlUnit(ff.getMacroType());
                    if (mu == null) {
                        log.error("MactoCtl " + ff.getTitle() + "：" + ff.getMacroType() + " is not exist.");
                        continue;
                    }

                    IFormMacroCtl ifmc = mu.getIFormMacroCtl();
                    ifmc.setIFormDAO(fdao);

                    macroCode = mu.getCode();

                    macroType = ifmc.getControlType();
                    controlText = ifmc.getControlText(userName, ff);
                    controlValue = ifmc.getControlValue(userName, ff);

                    // 须放在此位置，因为在当前用户宏控件的getContextValue中更改了ff的值
                    metaData = ifmc.getMetaData(ff);

                    options = ifmc.getControlOptions(userName, ff);
                    // options = options.replaceAll("\\\"", "");
                    if (options != null && !"".equals(options)) {
                        // options = options.replaceAll("\\\"", "");
                        try {
                            js = new JSONArray(options);
                        } catch (JSONException e) {
                            DebugUtil.e(getClass(), "execute", ff.getTitle() + " macro ctl's options cann't convert to JSONArray," + options);
                        }
                    }
                } else {
                    String fieldType = ff.getType();
                    if (fieldType != null && !"".equals(fieldType)) {
                        if ("DATE".equals(fieldType) || "DATE_TIME".equals(fieldType)) {
                            // 有可能已经通过表单域选择宏控件传值映射带过来了值
                            if (ff.getValue() != null && !"".equals(ff.getValue())) {
                                controlValue = ff.getValue();
                            } else {
                                controlValue = ff.getDefaultValueRaw();
                            }
                        } else {
                            if (ff.getValue() != null && !"".equals(ff.getValue())) {
                                controlValue = ff.getValue();
                            } else {
                                controlValue = ff.getDefaultValue();
                            }
                        }
                    } else {
                        controlValue = ff.getDefaultValue();
                    }
                }
                // 判断是否为意见输入框
                if (macroCode != null && !macroCode.equals("")) {
                    if ("macro_opinion".equals(macroCode) || "macro_opinionex".equals(macroCode)) {
                        if (controlText != null
                                && !controlText.trim().equals("")) {
                            opinionArr = new JSONArray(controlText);
                        }
                        if (controlValue != null
                                && !controlValue.trim().equals("")) {
                            opinionVal = new JSONObject(controlValue);
                        }
                    }

                    if ("nest_sheet".equals(macroCode) || "nest_table".equals(macroCode) || "macro_detaillist_ctl".equals(macroCode)) {
                        MacroCtlService macroCtlService = SpringUtil.getBean(MacroCtlService.class);
                        INestSheetCtl ntc = macroCtlService.getNestSheetCtl();
                        JSONObject jsonObj = ntc.getCtlDescription(ff);
                        if (jsonObj != null) {
                            field.put("desc", jsonObj);
                        }
                    } else if ("module_field_select".equals(macroCode)) {
                        MacroCtlService macroCtlService = SpringUtil.getBean(MacroCtlService.class);
                        IModuleFieldSelectCtl moduleFieldSelectCtl = macroCtlService.getModuleFieldSelectCtl();
                        com.alibaba.fastjson.JSONObject jsonObj = moduleFieldSelectCtl.getCtlDescription(ff);
                        if (jsonObj != null) {
                            field.put("desc", jsonObj);
                        }
                    }
                }
                field.put("type", ff.getType());
                if ("select".equals(ff.getType())) {
                    // options = fp.getOptionsOfSelect(fd, ff);
                    String[][] optionsArray = FormParser.getOptionsArrayOfSelect(fd, ff);
                    for (int i = 0; i < optionsArray.length; i++) {
                        String[] optionsItem = optionsArray[i];
                        if (optionsItem[1].equals(controlValue)) {
                            controlText = optionsItem[1];
                        }
                        JSONObject option = new JSONObject();
                        try {
                            option.put("value", optionsItem[1]);
                            option.put("name", optionsItem[0]);
                        } catch (Exception e) {
                            LogUtil.getLog(getClass()).error(e);
                        }
                        js.put(option);
                    }
                } else if ("radio".equals(ff.getType())) {
                    String[][] optionsArray = FormParser.getOptionsArrayOfRadio(fd, ff);
                    for (String[] optionsItem : optionsArray) {
                        if (optionsItem[0].equals(controlValue)) {
                            controlText = optionsItem[1];
                        }
                        JSONObject option = new JSONObject();
                        try {
                            option.put("value", optionsItem[0]);
                            option.put("name", optionsItem[1]);
                        } catch (Exception e) {
                            LogUtil.getLog(getClass()).error(e);
                        }
                        js.put(option);
                    }
                } else if (FormField.TYPE_BUTTON.equals(ff.getType())) {
                    String script = FormParser.getButtonScript(request, ff.getName(), fdao, ConstUtil.PAGE_TYPE_FLOW);
                    field.put("script", script);
                }

                field.put("options", js);
                field.put("text", controlText);
                String level = "";
                if ("checkbox".equals(ff.getType())) {
                    // level = "个人兴趣";
                    level = ff.getTitle();
                }
                field.put("level", level);
                field.put("macroType", macroType);
                field.put("editable", String.valueOf(ff.isEditable()));
                field.put("isHidden", String.valueOf(ff.isHidden()));
                field.put("isNull", String.valueOf(ff.isCanNull()));
                field.put("fieldType", ff.getFieldType());
                if (opinionVal != null) {
                    field.put("value", opinionVal);
                } else {
                    field.put("value", controlValue);
                }
                if (opinionArr != null && opinionArr.length() > 0) {
                    field.put("text", opinionArr);
                } else {
                    field.put("text", controlText);
                }
                field.put("macroCode", macroCode);

                // 可传SQL控件条件中的字段
                field.put("metaData", metaData);
                field.put("isReadonly", ff.isReadonly());

                fields.put(field);
            }
            // 遍历表单字段---------------------------------------------------------

            if (Global.getInstance().isDebug()) {
                DebugUtil.i(getClass(), "init 遍历表单字段", (System.currentTimeMillis() - t) + " ms");
            }

            // 置异或发散
            StringBuffer condBuf = new StringBuffer();
            boolean flagXorRadiate = wa.isXorRadiate();
            Vector vMatched = null;
            if (flagXorRadiate) {
                vMatched = WorkflowRouter.matchNextBranch(wa, userName, condBuf, mad.getId());
                String conds = condBuf.toString();
                boolean hasCond = !"".equals(conds); // 是否含有条件
                flagXorRadiate = hasCond;
            }
            json.put("flagXorRadiate", String.valueOf(flagXorRadiate));

            if (Global.getInstance().isDebug()) {
                DebugUtil.i(getClass(), "init flagXorRadiate", (System.currentTimeMillis() - t) + " ms");
            }

            Vector<WorkflowActionDb> vto = wa.getLinkToActions();
            JSONArray users = matchBranchAndUser(wa, vto);

            /*WorkflowRuler wr = new WorkflowRuler();
            // 取得下一步提交的用户--------------------------------------------------
            JSONArray users = new JSONArray();
            Iterator<UserDb> userir;
            Vector<WorkflowActionDb> vto = wa.getLinkToActions();
            for (WorkflowActionDb towa : vto) {
                if (towa.getJobCode().equals(WorkflowActionDb.PRE_TYPE_USER_SELECT)) {
                    JSONObject user = new JSONObject();
                    user.put("actionTitle", towa.getTitle());
                    user.put("roleName", towa.getJobName());
                    user.put("internalname", towa.getInternalName());
                    user.put("name", "WorkflowAction_" + towa.getId());
                    user.put("value", WorkflowActionDb.PRE_TYPE_USER_SELECT);
                    user.put("realName", "自选用户");
                    user.put("isSelectable", "true");
                    // 标志位，能否选择用户
                    boolean canSelUser = wr.canUserSelUser(request, towa);
                    user.put("canSelUser", String.valueOf(canSelUser));

                    boolean isStragegyGoDown = towa.isStrategyGoDown(); // 是否为下达
                    user.put("isGoDown", String.valueOf(isStragegyGoDown));

                    users.put(user);
                } else {
                    boolean isStrategySelectable = towa.isStrategySelectable();
                    boolean isStrategySelected = towa.isStrategySelected();
                    WorkflowRouter workflowRouter = new WorkflowRouter();
                    Vector<UserDb> vuser = workflowRouter.matchActionUser(request, towa, wa, false, mutilDept);
                    userir = vuser.iterator();
                    while (userir.hasNext()) {
                        UserDb ud = userir.next();
                        JSONObject user = new JSONObject();
                        user.put("actionTitle", towa.getTitle());
                        user.put("roleName", towa.getJobName());
                        user.put("internalname", towa.getInternalName());
                        user.put("name", "WorkflowAction_" + towa.getId());
                        user.put("value", ud.getName());
                        user.put("realName", ud.getRealName());
                        user.put("isSelectable", String.valueOf(isStrategySelectable));
                        user.put("isSelected", String.valueOf(isStrategySelected));

                        // 标志位，能否选择用户
                        boolean canSelUser = wr.canUserSelUser(request, towa);
                        user.put("canSelUser", String.valueOf(canSelUser));
                        users.put(user);
                    }
                }
            }*/
            result.put("users", users);

            // 如果下一节点上设置为“表单中的人员”，或者限定了部门表单域，则绑定change事件，以重新匹配人员
            List<String> bindFieldList = new ArrayList<>();
            for (WorkflowActionDb towa : vto) {
                String jobCode = towa.getJobCode();
                if (jobCode.startsWith(WorkflowActionDb.PRE_TYPE_FIELD_USER)) {
                    String fieldNames = jobCode.substring((WorkflowActionDb.PRE_TYPE_FIELD_USER + "_").length());
                    if (!fieldNames.startsWith("nest.")) {
                        String[] fieldAry = StrUtil.split(fieldNames, ",");
                        if (fieldAry == null) {
                            continue;
                        }
                        Collections.addAll(bindFieldList, fieldAry);
                    }
                } else {
                    String deptField = WorkflowActionDb.getActionProperty(wfp, towa.getInternalName(), "deptField");
                    if (!org.springframework.util.StringUtils.isEmpty(deptField)) {
                        bindFieldList.add(deptField);
                    }

                    // 限定部门表单域，只记录1个，即只支持单分支的情况
                    result.put("deptFieldLimited", deptField);
                    // 限定部门
                    String deptLimited = towa.getDept().trim();
                    result.put("deptLimited", deptLimited);
                }
            }
            result.put("bindFieldList", bindFieldList);

            if (Global.getInstance().isDebug()) {
                DebugUtil.i(getClass(), "init bindFieldList", (System.currentTimeMillis() - t) + " ms");
            }
        } catch (JSONException e) {
            LogUtil.getLog(getClass()).error(e);
            res = "-1";
            msg = "JSON解析异常";
            LogUtil.getLog(getClass()).error(e.getMessage());
        } catch (ErrMsgException e1) {
            res = "-1";
            msg = e1.getMessage();
            LogUtil.getLog(getClass()).error(e1.getMessage());
        } catch (MatchUserException e2) {
            res = "0";
            msg = "手机端兼职处理";
            String userName = authUtil.getUserName();
            DeptUserDb dud = new DeptUserDb();
            JSONArray deptArr = new JSONArray();
            try {
                Vector<DeptDb> vu = dud.getDeptsOfUser(userName);
                Iterator<DeptDb> irdu = vu.iterator();
                while (irdu.hasNext()) {
                    DeptDb dept = irdu.next();
                    if (dept.isHide()) {
                        continue;
                    }
                    String deptCode = dept.getCode();
                    String name = dept.getName();
                    if (!dept.getParentCode().equals(DeptDb.ROOTCODE) && !dept.getCode().equals(DeptDb.ROOTCODE)) {
                        name = dept.getDeptDb(dept.getParentCode()).getName() + "->" + dept.getName();
                    }
                    JSONObject deptObj = new JSONObject();
                    deptObj.put("name", name);
                    deptObj.put("code", deptCode);
                    deptArr.put(deptObj);
                }
                result.put("multiDepts", deptArr);
            } catch (JSONException e) {
                log.error(e2.getMessage());
            }
        } finally {
            try {
                json.put("res", res);
                json.put("msg", msg);
                result.put("fields", fields);
                json.put("result", result);
                json.put("hasAttach", hasAttach);

                // 算式相关的字段
                json.put("funcRelatedOnChangeFields", FuncUtil.doGetFieldsRelatedOnChangeMobile(fd));
            } catch (final JSONException e) {
                log.error(e.getMessage());
            }
        }
        return json;
    }

    @Override
    public JSONArray matchBranchAndUser(WorkflowActionDb wa, Vector<WorkflowActionDb> vto) throws JSONException, MatchUserException {
        JSONArray users = new JSONArray();
        WorkflowRuler wr = new WorkflowRuler();
        // 取得下一步提交的用户
        Iterator<UserDb> userir = null;
        for (WorkflowActionDb towa : vto) {
            if (towa.getJobCode().equals(
                    WorkflowActionDb.PRE_TYPE_USER_SELECT)
                    || towa.getJobCode().equals(WorkflowActionDb.PRE_TYPE_USER_SELECT_IN_ADMIN_DEPT)) {
                boolean isStragegyGoDown = towa.isStrategyGoDown(); // 是否为下达

                JSONObject user = new JSONObject();
                user.put("actionTitle", towa.getTitle());
                user.put("roleName", towa.getJobName());
                user.put("internalname", towa.getInternalName());
                user.put("name", "WorkflowAction_" + towa.getId());
                // 手机客户端还不能区分是否在所管理的部门范围内
                user.put("value", WorkflowActionDb.PRE_TYPE_USER_SELECT);
                user.put("realName", "自选用户");
                user.put("isSelectable", "true");

                // 如果节点上曾经选过人，则在手机客户端默认选中
                user.put("actionUserName", towa.getUserName());
                user.put("actionUserRealName", towa.getUserRealName());

                // 标志位，能否选择用户
                boolean canSelUser = wr.canUserSelUser(request, towa);
                // LogUtil.getLog(getClass()).info(getClass() + " actionUserRealName=" +
                // towa.getUserRealName() + " canSelUser=" + canSelUser);
                user.put("canSelUser", String.valueOf(canSelUser));
                user.put("isGoDown", String.valueOf(isStragegyGoDown));

                users.put(user);
            } else {
                boolean isStrategySelectable = towa.isStrategySelectable();
                boolean isStrategySelected = towa.isStrategySelected();
                WorkflowRouter workflowRouter = new WorkflowRouter();
                Vector<UserDb> vuser = workflowRouter.matchActionUser(request, towa, wa, false, null);
                int userCount = vuser.size();
                userir = vuser.iterator();
                while (userir.hasNext()) {
                    UserDb ud = userir.next();
                    JSONObject user = new JSONObject();
                    user.put("actionTitle", towa.getTitle());
                    user.put("roleName", towa.getJobName());
                    user.put("internalname", towa.getInternalName());
                    user.put("name", "WorkflowAction_" + towa.getId());
                    user.put("value", ud.getName());
                    user.put("realName", ud.getRealName());
                    user.put("isSelectable", String.valueOf(isStrategySelectable));

                    if (userCount == 1 && vto.size() == 1) {
                        user.put("isSelected", "true");
                    } else {
                        user.put("isSelected", String.valueOf(isStrategySelected));
                    }

                    // 标志位，能否选择用户
                    boolean canSelUser = wr.canUserSelUser(request, towa);
                    user.put("canSelUser", String.valueOf(canSelUser));
                    users.put(user);
                }
            }
        }
        return users;
    }

    @Override
    public JSONArray fieldsToJSONArray(long myActionId, String skey, Leaf lf, IFormDAO fdao, Vector<FormField> formFields, String[] fdsWrite, String[] fdsHide, JSONArray arrSums) throws JSONException {
        Privilege privilege = new Privilege();
        FormDb fd = fdao.getFormDb();
        org.jsoup.nodes.Document formDoc = Jsoup.parse(fd.getContent());

        ModuleSetupDb moduleSetupDb = new ModuleSetupDb();
        moduleSetupDb = moduleSetupDb.getModuleSetupDb(fdao.getFormCode());

        JSONArray fields = new JSONArray();
        MacroCtlMgr mm = new MacroCtlMgr();
        for (FormField ff : formFields) {
            if (ff.getType().equals(FormField.TYPE_BUTTON)) {
                com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(ff.getDescription());
                com.alibaba.fastjson.JSONObject jsonObj = ModuleUtil.getButtonParsed(request, jsonObject, moduleSetupDb, fdao, ConstUtil.PAGE_TYPE_FLOW);
                // 无权查看时返回 null
                if (jsonObj == null) {
                    continue;
                }
            }

            String val = StrUtil.getNullStr(fdao.getFieldValue(ff.getName()));
            boolean finded = false;
            if (fdsWrite != null) {
                for (String value : fdsWrite) {
                    if (ff.getName().equals(value)) {
                        finded = true;
                        break;
                    }
                }
            }
            ff.setEditable(finded);

            // 如果不是自由流程
            if (lf.getType() != Leaf.TYPE_FREE) {
                // 置隐藏表单域
                finded = false;
                if (fdsHide != null) {
                    for (String s : fdsHide) {
                        if (ff.getName().equals(s)) {
                            finded = true;
                            break;
                        }
                    }
                }
                if (finded) {
                    ff.setHidden(true);
                }
            }

            JSONObject field = new JSONObject();
            field.put("title", ff.getTitle());
            field.put("code", ff.getName());
            field.put("desc", StrUtil.getNullStr(ff.getDescription()));

            field.put("present", StrUtil.getNullStr(ff.getPresent()));
            // 如果是计算控件，则取出精度和四舍五入属性
            if (ff.getType().equals(FormField.TYPE_CALCULATOR)) {
                FormParser fp = new FormParser();
                String isroundto5 = fp.getFieldAttribute(fd, ff, "isroundto5");
                String digit = fp.getFieldAttribute(fd, ff, "digit");
                field.put("formula", ff.getDefaultValueRaw());
                field.put("isroundto5", isroundto5);
                field.put("digit", digit);
            }

            String options = "";
            String macroType = "";
            String controlText = "";
            String macroCode = "";
            JSONArray js = new JSONArray();
            JSONArray opinionArr = null;
            JSONObject opinionVal = null;

            String metaData = "";
            if ("macro".equals(ff.getType())) {
                MacroCtlUnit mu = mm.getMacroCtlUnit(ff.getMacroType());
                if (mu == null) {
                    log.error("MactoCtl " + ff.getTitle() + "：" + ff.getMacroType() + " is not exist.");
                    continue;
                }

                macroCode = mu.getCode();

                IFormMacroCtl ifmc = mu.getIFormMacroCtl();
                ifmc.setIFormDAO(fdao);
                ifmc.setMyActionId(myActionId);
                macroType = ifmc.getControlType();

                // 如果值为null，则在json中put的时候，是无效的，不会被记录至json中
                controlText = StrUtil.getNullStr(ifmc.getControlText(privilege.getUserName(skey), ff));
                val = StrUtil.getNullStr(ifmc.getControlValue(privilege.getUserName(skey), ff));
                // 须放在此位置，因为在当前用户宏控件的getContextValue中更改了ff的值
                metaData = ifmc.getMetaData(ff);

                options = ifmc.getControlOptions(privilege.getUserName(skey), ff);
                if (options != null && !"".equals(options)) {
                    // options = options.replaceAll("\\\"", "");
                    js = new JSONArray(options);
                }
            }
            // 判断是否为意见输入框
            if (macroCode != null && !"".equals(macroCode)) {
                if ("macro_opinion".equals(macroCode) || "macro_opinionex".equals(macroCode)) {
                    if (controlText != null
                            && !"".equals(controlText.trim())) {
                        opinionArr = new JSONArray(controlText);
                    }
                    if (val != null && !"".equals(val.trim())) {
                        opinionVal = new JSONObject(val);
                    }
                }

                if ("nest_sheet".equals(macroCode) || "nest_table".equals(macroCode) || "macro_detaillist_ctl".equals(macroCode)) {
                    MacroCtlService macroCtlService = SpringUtil.getBean(MacroCtlService.class);
                    INestSheetCtl ntc = macroCtlService.getNestSheetCtl();
                    JSONObject jsonObj = ntc.getCtlDescription(ff);
                    if (jsonObj != null) {
                        field.put("desc", jsonObj);
                    }

                    String destForm = jsonObj.getString("destForm");
                    ModuleSetupDb msd = new ModuleSetupDb();
                    msd = msd.getModuleSetupDb(destForm);
                    FormDb fdNest = new FormDb();
                    fdNest = fdNest.getFormDb(msd.getString("form_code"));
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("formCode", msd.getString("form_code"));
                    jsonObject.put("sums", FormUtil.getSums(fdNest, fd, String.valueOf(fdao.getId())));
                    arrSums.put(jsonObject);
                } else if ("module_field_select".equals(macroCode)) {
                    MacroCtlService macroCtlService = SpringUtil.getBean(MacroCtlService.class);
                    IModuleFieldSelectCtl moduleFieldSelectCtl = macroCtlService.getModuleFieldSelectCtl();
                    com.alibaba.fastjson.JSONObject jsonObj = moduleFieldSelectCtl.getCtlDescription(ff);
                    if (jsonObj != null) {
                        field.put("desc", jsonObj);
                    }
                }
            }
            field.put("type", ff.getType());
            if ("select".equals(ff.getType())) {
                String[][] optionsArray = FormParser.getOptionsArrayOfSelect(fd, ff);
                for (String[] optionsItem : optionsArray) {
                    if (optionsItem[1].equals(val)) {
                        controlText = optionsItem[0];
                    }
                    if (optionsItem.length == 2) {
                        JSONObject option = new JSONObject();
                        option.put("value", optionsItem[1]);
                        option.put("name", optionsItem[0]);
                        js.put(option);
                    }
                }
            } else if ("radio".equals(ff.getType())) {
                String[][] optionsArray = FormParser.getOptionsArrayOfRadio(fd, ff);
                for (String[] optionsItem : optionsArray) {
                    if (optionsItem[0].equals(val)) {
                        controlText = optionsItem[1];
                    }
                    JSONObject option = new JSONObject();
                    try {
                        option.put("value", optionsItem[0]);
                        option.put("name", optionsItem[1]);
                    } catch (Exception e) {
                        LogUtil.getLog(getClass()).error(e);
                    }
                    js.put(option);
                }
            } else if (FormField.TYPE_BUTTON.equals(ff.getType())) {
                String script = FormParser.getButtonScript(request, ff.getName(), fdao, ConstUtil.PAGE_TYPE_FLOW);
                field.put("script", script);
            }
            String level = "";
            if ("checkbox".equals(ff.getType())) {
                level = ff.getTitle();
                Elements els = formDoc.select("input[name='" + ff.getName() + "']");
                if (els.size() > 0) {
                    field.put("switch", els.get(0).attr("switch"));
                }
            }
            field.put("options", js);
            if (opinionVal != null) {
                field.put("value", opinionVal);
            } else {
                if (!StringUtils.isEmpty(val)) {
                    if (ff.getFieldType() == FormField.FIELD_TYPE_DOUBLE || ff.getFieldType() == FormField.FIELD_TYPE_FLOAT) {
                        if (FormField.FORMAT_DIGIT.equals(ff.getFormat())) {
                            DecimalFormat formater = new DecimalFormat();
                            formater.setMaximumFractionDigits(ff.getDigitNum());
                            formater.setGroupingSize(0);
                            formater.setRoundingMode(RoundingMode.FLOOR);
                            val = formater.format(StrUtil.toDouble(ff.getValue()));
                        } else if (FormField.FORMAT_DIGIT_AND_ROUND.equals(ff.getFormat())) {
                            BigDecimal bd = BigDecimal.valueOf(StrUtil.toDouble(ff.getValue()));
                            val = String.valueOf(bd.setScale(ff.getDigitNum(), BigDecimal.ROUND_HALF_UP).doubleValue());
                        }
                    }
                }

                field.put("value", val);
            }
            if (opinionArr != null && opinionArr.length() > 0) {
                field.put("text", opinionArr);
            } else {
                field.put("text", controlText);
            }
            // log.info(ff.getTitle() +
            // " controlText=" + controlText);
            field.put("level", level);
            field.put("macroType", macroType);
            field.put("editable", String.valueOf(ff.isEditable()));
            field.put("isHidden", String.valueOf(ff.isHidden()));
            field.put("isNull", String.valueOf(ff.isCanNull()));
            field.put("fieldType", ff.getFieldType());
            field.put("macroCode", macroCode);
            // 可传SQL控件条件中的字段
            field.put("metaData", metaData);
            field.put("isReadonly", ff.isReadonly());
            // 表单域选择宏控件已经置入了desc
            if (!field.has("desc")) {
                field.put("desc", ff.getDescription());
            }
            fields.put(field);
        }
        return fields;
    }

    public com.alibaba.fastjson.JSONObject listDoing(String showyear, String showmonth, String op, String title, int pagenum, int pagesize) {
        String beginDate = "", endDate = "";
        if (!"".equals(showyear) && !"".equals(showmonth)) {
            Calendar cal = Calendar.getInstance();
            cal.set(Calendar.YEAR, StrUtil.toInt(showyear));
            cal.set(Calendar.MONTH, StrUtil.toInt(showmonth) - 1);
            cal.set(Calendar.DAY_OF_MONTH, 1);
            beginDate = DateUtil.format(cal, "yyyy-MM-dd");
            cal.add(Calendar.MONTH, 1);
            endDate = DateUtil.format(cal, "yyyy-MM-dd");
        }
        Privilege privilege = new Privilege();
        String myname = authUtil.getUserName();
        MyActionDb mad = new MyActionDb();
        WorkflowDb wfd = new WorkflowDb();

        Config cfg = Config.getInstance();
        boolean isFlowProxy = cfg.getBooleanProperty("isFlowProxy");

        String sql = "select m.id from flow_my_action m, flow f where m.flow_id=f.id and (m.user_name=" + StrUtil.sqlstr(myname);
        if (isFlowProxy) {
            sql += " or m.proxy=" + StrUtil.sqlstr(myname);
        }
        sql += ") and f.status<>" + WorkflowDb.STATUS_NONE + " and f.status<> " + WorkflowDb.STATUS_DELETED + " and (is_checked=0 or is_checked=2) and sub_my_action_id=0";
        if ("search".equals(op)) {
            title = title.trim();
            sql = "select m.id from flow_my_action m, flow f where m.flow_id=f.id and f.status<>" + WorkflowDb.STATUS_NONE + " and f.status<>" + WorkflowDb.STATUS_DELETED + " and f.status<>" + WorkflowDb.STATUS_DISCARDED + " and (m.user_name=" + StrUtil.sqlstr(myname);
            if (isFlowProxy) {
                sql += " or m.proxy=" + StrUtil.sqlstr(myname);
            }
            sql += ") and (is_checked=0 or is_checked=2) and sub_my_action_id=0";
            if (!"".equals(title)) {
                if (StrUtil.isNumeric(title)) {
                    sql += " and f.id=" + title;
                } else {
                    sql += " and f.title like " + StrUtil.sqlstr("%" + title + "%");
                }
            }
            if (!"".equals(beginDate)) {
                sql += " and f.mydate>=" + SQLFilter.getDateStr(beginDate, "yyyy-MM-dd");
            }
            if (!"".equals(endDate)) {
                sql += " and f.mydate<" + SQLFilter.getDateStr(endDate, "yyyy-MM-dd");
            }
        }

        sql += " order by receive_date desc";

        com.alibaba.fastjson.JSONObject result = new com.alibaba.fastjson.JSONObject();

        //LogUtil.getLog(getClass()).info("sql = " + sql);
        int curpage = pagenum; // 第几页
        ListResult lr;
        try {
            lr = mad.listResult(sql, curpage, pagesize);
            long total = lr.getTotal();

            Vector v = lr.getResult();
            Iterator ir = null;
            if (v != null) {
                ir = v.iterator();
            }
            result.put("total", total);
            result.put("count", pagesize);

            Leaf lf = new Leaf();

            com.alibaba.fastjson.JSONArray flows = new com.alibaba.fastjson.JSONArray();
            while (ir.hasNext()) {
                mad = (MyActionDb) ir.next();

                wfd = wfd.getWorkflowDb((int) mad.getFlowId());

                lf = lf.getLeaf(wfd.getTypeCode());
                if (lf == null) {
                    lf = new Leaf();
                    continue;
                }

                com.alibaba.fastjson.JSONObject flow = new com.alibaba.fastjson.JSONObject();
                flow.put("myActionId", String.valueOf(mad.getId()));
                flow.put("flowId", String.valueOf(mad.getFlowId()));
                flow.put("name", StringEscapeUtils.unescapeHtml3(wfd.getTitle()));
                flow.put("status", WorkflowActionDb.getStatusName(mad.getActionStatus()));
                flow.put("beginDate", DateUtil.format(wfd.getBeginDate(), "MM-dd HH:mm"));
                flow.put("type", String.valueOf(lf.getType()));
                flow.put("typeName", lf.getName());
                flow.put("isReaded", mad.isReaded());
                flows.add(flow);
            }
            result.put("flows", flows);
        } catch (ErrMsgException e) {
            LogUtil.getLog(getClass()).error(e);
        }
        return result;
    }
}
